package com.lagou.edu.component;

import com.lagou.annotation.Autowired;
import com.lagou.annotation.Service;
import com.lagou.annotation.Transactional;
import com.lagou.edu.factory.ProxyFactory;
import com.lagou.edu.utils.TransactionManager;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ComponentScan {

    private String basePackage;

    public String getBasePackage() {
        return basePackage;
    }

    public void setBasePackage(String basePackage) {
        this.basePackage = basePackage;
    }

    public void scanBean(Map<String, Object> beanMap) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        if (basePackage.isEmpty())
            throw new RuntimeException("扫描包路径不正确");
        List<String> classPaths = getClassPath();
        doScan(classPaths, beanMap);

    }

    private void doScan(List<String> classPaths, Map<String, Object> beanMap) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        for (String classPath : classPaths) {
            Class<?> classType = Class.forName(classPath);
            Service service = classType.getAnnotation(Service.class);
            if (service != null) {
                Object object = classType.newInstance();
                String id = service.value();
                beanMap.put(id, object);
            }
        }
        dependency(beanMap);
        doTransaction(beanMap);
    }

    /**
     * 依赖注入
     * @param beanMap
     */
    private void dependency(Map<String, Object> beanMap) {
        beanMap.entrySet().forEach(entry -> {
            String id = entry.getKey();
            Object object = entry.getValue();
            Class<?> classType = object.getClass();
            Field [] fields = classType.getDeclaredFields();
            for (Field field : fields) {
                String fieldName = field.getName();
                Autowired autowried = field.getAnnotation(Autowired.class);
                if ( autowried!= null) {
                    String beanName = autowried.value();
                    beanName  = beanName == null ? fieldName: beanName;
                    Method [] methods = classType.getDeclaredMethods();
                    for (Method method : methods) {
                        if (method.getName().equalsIgnoreCase("set" + fieldName)) {
                            try {
                                method.invoke(object, beanMap.get(beanName));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        });
    }

    /**
     * 事务处理
     * @param beanMap
     */
    private void doTransaction(Map<String, Object> beanMap) {
        beanMap.entrySet().forEach(entry -> {
            String id = entry.getKey();
            Object object = entry.getValue();
            Class<?> classType = object.getClass();
            Transactional transactional = classType.getDeclaredAnnotation(Transactional.class);
            TransactionManager transactionManager = (TransactionManager) beanMap.get("transactionManager");
            if (transactional != null) {
                ProxyFactory proxyFactory = new ProxyFactory();
                proxyFactory.setTransactionManager(transactionManager);
                Object proxyObject = proxyFactory.getCglibProxy(object);
                beanMap.put(id, proxyObject);
            }
        });
    }

    private List<String> getClassPath() {
        String path = basePackage.replace(".", "/");
        String basePath = ComponentScan.class.getResource("/").getPath();
        File directory = new File(basePath + path);
        return  getClassPath(basePackage, directory.listFiles());

    }

    private List<String> getClassPath(String parentPath, File [] files) {
        List<String> paths = new ArrayList<>();
        for (File file : files) {
            String fileName = file.getName();
            String path = parentPath + "." + fileName;
            if (file.isDirectory()) {
                paths.addAll(getClassPath(path, file.listFiles()));
            }

            if (fileName.endsWith(".class")) {
                path = path.substring(0, path.length() - 6);
                paths.add(path);
            }
        }
        return paths;
    }

    public static void main(String[] args) {
        ComponentScan componentScan = new ComponentScan();
        componentScan.setBasePackage("com.lagou.edu.annotation");
        System.out.println(componentScan.getClassPath());
    }
}
